#ifndef Net_Socket_INCLUDED
#define Net_Socket_INCLUDED

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <vector>

#include "socketaddress.h"

using std::vector;
static const int INVALID_SOCKET = -1;

class Socket
{
public:
    enum SelectMode
    {
        SELECT_READ  = 1,
        SELECT_WRITE = 2,
        SELECT_ERROR = 4
    };
    enum SocketConst
    {
        LISTEN_BACKLOG=10240,
        MAXOPENFD=10*1024*1024
    };
    /*
    // valid __socket_type
    SOCK_STREAM = 1,        // Sequenced, reliable, connection-based byte streams.  
    SOCK_DGRAM = 2,         //Connectionless, unreliable datagrams of fixed maximum length.  
    SOCK_RAW = 3,           //Raw protocol interface.  
    SOCK_RDM = 4,           //Reliably-delivered messages. 
    SOCK_SEQPACKET = 5,     //Sequenced, reliable, connection-based, datagrams of fixed maximum length. 
    SOCK_DCCP = 6,          //Datagram Congestion Control Protocol. 
    SOCK_PACKET = 10,       //Linux specific way of getting packets at the dev level.  For writing rarp and
                            //other similar things on the user level. 
    */
    Socket(__socket_type sockettype = SOCK_STREAM);
    Socket(int sockfd,__socket_type sockettype = SOCK_STREAM);
    Socket(const Socket& socket);
    Socket& operator = (const Socket& socket);
    // 允许复制，但只需要在一个地方手动调用 close
    
    virtual ~Socket();
        
    void close();
    
    bool poll(unsigned int timeout_ms, int mode);
    int acceptConnection();
    int connect(const SocketAddress& address);
    int connect(const SocketAddress& address, unsigned int timeout_ms);
    int connectNB(const SocketAddress& address);
    int bind(const SocketAddress& address, bool reuseAddress = false);
    int listen(int backlog = LISTEN_BACKLOG);

    void shutdownRecv();
    void shutdownSend();
    void shutdown();
    
    int sendBytes(const char* buffer, unsigned int length, int flags = 0);
    int recvBytes(char* buffer, unsigned int length, int flags = 0);
    int sendTo(const char* buffer, unsigned int length, const SocketAddress& address, int flags = 0);
    int recvFrom(char* buffer, unsigned int length, SocketAddress& address, int flags = 0);
    // for convenience
    void setOption(int level, int option, const void* value, int length);
    void getOption(int level, int option, void* value,unsigned int& length);
    void sendUrgent(unsigned char data);
    // 可读取字节数
    void setSendBufferSize(unsigned int size);
    unsigned int getSendBufferSize();
    void setRecvBufferSize(unsigned int size);
    unsigned int getRecvBufferSize();
    void setSendTimeout(timeval& val);
    void getSendTimeout(timeval& val);
    void setRecvTimeout(timeval& val);
    void getRecvTimeout(timeval& val);
    SocketAddress address();
    SocketAddress peerAddress();
    void setLinger(bool on, int seconds);
    void getLinger(bool& on, int& seconds);
    void setNoDelay(bool flag);
    bool getNoDelay();
    void setKeepAlive(bool flag);
    bool getKeepAlive();
    void setReuseAddress(bool flag);
    bool getReuseAddress();
    void setReusePort(bool flag);
    bool getReusePort();
    void setOOBInline(bool flag);
    bool getOOBInline();
    void setBroadcast(bool flag);
    bool getBroadcast();
    
    int socketError();
    int sockfd();
    void ioctl(int request, int& arg);
    void ioctl(int request, void* arg);
    bool initialized();
    void init(int af);
    int initSocket(int af, int type, int proto = 0);
    
    __socket_type getSocketType();
    void setSocketType(__socket_type sockettype);
public:
    // statics
    static int select(vector<Socket>& readList,
                 vector<Socket>& writeList,
                 vector<Socket>& exceptList,
                 unsigned int timeout_ms);
    
    static void setOption(int sockfd,int level, int option, const void* value, int length);
    static void getOption(int sockfd,int level, int option, void* value, unsigned int& length);
    static void setNoDelay(int sockfd,bool flag);
    static bool getNoDelay(int sockfd);
    static void setBlocking(int sockfd,bool flag);
    static bool getBlocking(int sockfd);
    static int fcntl(int sockfd,int request);
    static int fcntl(int sockfd,int request, long arg);
    static int available(int fd);

private:
    
    int                 m_sockfd;
    __socket_type       m_socketType;
};


/*
 * inlines
 */
inline int Socket::sockfd()
{
    return m_sockfd;
}

inline bool Socket::initialized()
{
    return m_sockfd != INVALID_SOCKET;
}

inline __socket_type Socket::getSocketType()
{
    return m_socketType;
}

inline void Socket::setSocketType(__socket_type sockettype)
{
    m_socketType = sockettype;
}

#endif















